home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Libraries / SAT 2.4.0 / SAT / Demo ƒ / Collision ⁄⁄⁄ demo ƒ / Collision ⁄⁄⁄.p < prev    next >
Encoding:
Text File  |  1997-03-12  |  37.1 KB  |  1,312 lines  |  [TEXT/PJMM]

  1. {Collision ///}
  2. {}
  3. {This demo demonstrates some alternative ways to use SAT:}
  4. {}
  5. {• The animation is called from the standard event loop (via TransSkel). This slows things down}
  6. {quite a bit (since all other processes are allowed to run), but makes the application background-}
  7. {friendly.}
  8. {• It runs in an ordinary, moveable window. We can, with some effort, do this while still}
  9. {using the fast mode (in which case we would have to restrict window dragging so it stays}
  10. {within the main screen, modify certain global (gSAT.ox ad gSAT.oy), and also limit the}
  11. {horizontal alignment of the window like HyperCard does), but in this demo we just use the}
  12. {slow (safe) mode. }
  13. {• We create sprites from QuickDraw calls instead of cicns.}
  14. {• We use the mask regions of the sprites for better collision detection.}
  15. {• We use a pattern as backkground instead of PICTs.}
  16. {• All the code is in one unit. This might make it less structured, less encapsulated, but I wanted}
  17. {to show you that you don't have to do things exactly the way I do in the other demos.}
  18. {• Using a modified sprite record.}
  19. {• Fixed-point positions}
  20. {}
  21. {However, some variations remain that aren't demonstrated even here:}
  22. {• Calculating the positions of the sprites with the system clock (TickCount or Time Manager) instead of}
  23. {moving them each frame. That approach has some advantages (i.e. constant speed on objects), can easily}
  24. {be used with SAT, but is not as simple.}
  25.  
  26.  
  27. program CollisionIII;
  28.  
  29.     uses
  30. {$ifc UNDEFINED THINK_PASCAL}
  31.         Types, QuickDraw, Events, Windows, Dialogs, Fonts, DiskInit, TextEdit,{}
  32.         Traps, Memory, SegLoad, Scrap, ToolUtils, OSUtils, Menus, Resources,{}
  33.         StandardFile, GestaltEqu, Files, Errors, Controls, TextUtils, QuickDrawText,
  34. {$elsec}
  35.         InterfacesUI,  {To give Think Pascal some UPI}
  36. {$endc}
  37.         TransSkel, SAT;
  38.  
  39. {A modified sprite record. This should usually go in some globals unit}
  40. {in your project.}
  41. type
  42.         C3SpritePtr = ^C3Sprite;
  43.         C3Sprite = record
  44. { Variables that you should change as appropriate }
  45.                 kind: Integer; { Used for identification. >0: friend. <0 foe }
  46.                 position: Point;
  47.                 hotRect, hotRect2: Rect; { Tells how large the sprite is; hotRect is centered around origo }
  48.                                         {hotRect is set by you. hotRect2 is offset to the current position.}
  49.                 face: FacePtr; { Pointer to the Face (appearance) to be used. }
  50.                 task: ProcPtr; { Callback-routine, called once per frame. If task=nil, the sprite is removed. }
  51.                 hitTask: ProcPtr; { Callback in collisions. }
  52.                 destructTask: ProcPtr; { Called when a sprite is disposed. (Usually nil.) }
  53.                 clip: RgnHandle;
  54. { SAT variables that you shouldn't change: }
  55.                 oldpos: Point;                {The 'task' routine is not allowed to change this! }
  56.                 next, prev: SpritePtr;    {You may change them in your own sorting routine, but be careful if you do.}
  57.                 r, oldr: Rect;                {Rectangle telling where to draw. Avoid messing with it.}
  58.                 oldFace: FacePtr;            {Used by RunSAT2}
  59.                 dirty: Boolean;            {Used by RunSAT2}
  60. {Variables for internal use by the sprites. I have edited them, to add fixed-point postions!}
  61. {Since we have edited the record, we must SetSpriteSize immediately}
  62. {after initializing (before any sprites are created)!}
  63.                 layer: integer; {For layer-sorting. When not used for that, use freely.}
  64.                 speed: Point; { Can be used for speed, but not necessarily. }
  65.                 mode: integer; { Usually used for different modes and/or to determine what image to show next. }
  66.                 fixedPos: Point; {Position * 16}
  67.             end;
  68.  
  69.     const
  70.         newgameItem = 1;
  71.         clearHighItem = 4;
  72.  
  73.         aboutAlrt = 128;
  74.         fileMenuRes = 128;
  75.         shapeMenuRes = 129;
  76.         theWindRes = 128;
  77.  
  78.         kGameTime = 3600; {60 sekunder}
  79.         kExtraTime = 180; {3 sekunder}
  80.         kLevelBonus = 25;
  81.  
  82.     type
  83.         SettingsRec = record
  84.                 high: Longint;
  85.                 player: string[5];
  86.             end;
  87.         SettingsPtr = ^SettingsRec;
  88.         SettingsHnd = ^SettingsPtr;
  89.     var
  90.         settings: SettingsHnd;
  91.         fileMenu, shapeMenu: MenuHandle;
  92.         gameRunning: Boolean;
  93.         gameStartTime, lastSetStartTime: Longint;
  94.         setCount: integer;
  95.         gMode: integer;
  96.         scoreFace, highFace, lastface: FacePtr;
  97.         myFace, welcomeFace: FacePtr;
  98.         score: Longint;
  99.         bgPat: SATPatHandle;
  100.  
  101.         scaledFace: array[0..31] of FacePtr;
  102.  
  103.     procedure Barf;
  104.     begin
  105.         SATReportStr('Something went wrong. Sorry.');
  106.         halt;
  107.     end;
  108.  
  109. {Ljud:}
  110.  
  111. {Konstruera en snd-resurs artificiellt}
  112. {Rutinen bygger en handle med reserverad plats för ljudet, som sedan värdrutinen kan skapa.}
  113.     function CreateSnd (size: longint; var sndH: handle; var dataPek: Ptr): Boolean;
  114.         type
  115.             mySndRec = packed record
  116.                     format: integer;
  117.                     numsynth: integer; {must be 0}
  118. {synth}
  119.                     synthid: integer;{5}
  120.                     synthinit: longint;{0}
  121.  
  122.                     numcom: integer; {must be 1}
  123. {command}
  124.                     command: integer;{ $8051}
  125.                     param1: integer; {0}
  126.                     param2: longint; { $14}
  127. {sound header}
  128.                     dataptr: Ptr;
  129.                     datasize: longint;
  130.                     samplerate: longint; {22kHz = $56ee8ba3}
  131.                     loopstart: Ptr;
  132.                     loopend: Ptr;
  133.                     encoding: Byte;{0}
  134.                     basenote: Byte; { $3c}
  135. {data}
  136.                     ljud: packed array[0..0] of Byte;
  137.                 end;
  138.             msrp = ^mySndRec;
  139.             msrh = ^msrp;
  140.         var
  141.             h: msrh;
  142.     begin
  143.         h := msrh(NewHandle(sizeof(mySndRec) + size));
  144.         if h = nil then
  145.             CreateSnd := false
  146.         else
  147.             begin
  148.                 HLock(Handle(h)); {Fixar detta buggen med att ljuden ändras?}
  149.                 with h^^ do
  150.                     begin
  151.                         format := 1;
  152.                         numsynth := 1;
  153.                         synthid := 5;
  154.                         synthinit := 0;
  155.                         numcom := 1;
  156.                         command := $8051;
  157.                         param1 := 0;
  158.                         param2 := $14;
  159.                         dataptr := @ljud[0];
  160.                         datasize := size;
  161.                         samplerate := $56ee8ba3; {div 2 - fast varför köra 11kHz när man synthar?!}
  162.                         loopstart := dataptr;
  163.                         loopend := dataptr; {?}
  164.                         encoding := 0;
  165.                         basenote := $3c;
  166.                         dataPek := dataptr; {Utdata}
  167.                     end; {with}
  168.                 SndH := handle(h);{utdata}
  169.                 CreateSnd := true;
  170.             end; {if nil else}
  171.     end;{CreateSnd}
  172.  
  173.     var
  174.         pushH, bippH, baeH: Handle;
  175.  
  176. {Fixa några bra subrutiner för ljudsyntning?!}
  177. {- Eko}
  178. {- Lågpass och högpass}
  179. {- Sampla upp eller ner?}
  180. {- Frekvensvariation?}
  181. {- Fade in, fade out (mm envelope)}
  182. {Drömmen är förstås FFT, så man kan göra riktigt vass bandspärr, frekvensskift mm.}
  183. {Apropå: kan man inte göra bra ljudkompression med FFT?}
  184.  
  185. {$PUSH}
  186. {$R-}
  187.  
  188. {Rutinen som skall bygga de syntetiska ljud vi önskar!}
  189.     procedure Synth;
  190.         type
  191.             ArtRec = record
  192.                     arr: packed array[0..10000] of Byte;
  193.                 end;
  194.             ArtPtr = ^ArtRec;
  195.         var
  196.             tmpPtr: ArtPtr;
  197.             i: integer;
  198.         const
  199.             pushSize = 3479;
  200.             bippSize = 2959;
  201.             baeSize = 20000;
  202.     begin
  203.         if not CreateSnd(pushSize + 1, pushH, Ptr(tmpptr)) then
  204.             CheckNoMem(nil); {EmergencyExit}
  205.         for i := 0 to pushSize do
  206.             tmpptr^.arr[i] := band(char(random), 127) * (pushSize - i) div pushSize + 128;
  207.         for i := 0 to pushSize - 3 do
  208.             tmpptr^.arr[i] := (tmpptr^.arr[i] + tmpptr^.arr[i + 1] + tmpptr^.arr[i + 2] + tmpptr^.arr[i + 3]) div 4;
  209.         for i := 0 to 64 do
  210.             begin
  211. {tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;}
  212.                 tmpptr^.arr[pushSize - i] := tmpptr^.arr[pushSize - i] * i div 64;
  213.             end;
  214.  
  215.         if not CreateSnd(bippSize + 1, bippH, Ptr(tmpptr)) then
  216.             CheckNoMem(nil); {EmergencyExit}
  217.         for i := 0 to bippSize do
  218.             tmpptr^.arr[i] := i mod (i div 171 + 1) mod 127 + 128; {mjiioo}
  219. {tmpptr^.arr[i] := i mod (i div 171 + 1) + 128; {mjiioo}
  220. {tmpptr^.arr[i] := i mod (i div 17 + 1) + 128; {maipp}
  221. {tmpptr^.arr[i] := band(i, 63) + 128;}
  222.         for i := 0 to 64 do
  223.             begin
  224.                 tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;
  225.                 tmpptr^.arr[bippSize - i] := tmpptr^.arr[bippSize - i] * i div 64;
  226.             end;
  227.  
  228.         if not CreateSnd(baeSize + 1, baeH, Ptr(tmpptr)) then
  229.             CheckNoMem(nil); {EmergencyExit}
  230.         for i := 0 to baeSize do
  231.             tmpptr^.arr[i] := (i div 5) mod (i div 1571 + 1) mod 127 + 128; {mjiioo}
  232. {tmpptr^.arr[i] := i mod (i div 171 + 1) + 128; {mjiioo}
  233. {tmpptr^.arr[i] := i mod (i div 17 + 1) + 128; {maipp}
  234. {tmpptr^.arr[i] := band(i, 63) + 128;}
  235.         for i := 0 to baeSize - 3 do
  236.             tmpptr^.arr[i] := (tmpptr^.arr[i] + tmpptr^.arr[i + 1] + tmpptr^.arr[i + 2] + tmpptr^.arr[i + 3]) div 4;
  237.         for i := 0 to 64 do
  238.             begin
  239.                 tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;
  240.                 tmpptr^.arr[baeSize - i] := tmpptr^.arr[baeSize - i] * i div 64;
  241.             end;
  242.     end;
  243. {$POP}
  244.  
  245.     procedure DoAbout;
  246.     begin
  247.         if 1 = Alert(aboutAlrt, nil) then
  248.             ;
  249.     end;
  250.  
  251. {Two handly routines from my dialog utilities unit.}
  252.     procedure SetTextDItem (theDialog: DialogPtr; itemNo: integer; theString: Str255);
  253.         var
  254.             kind: integer;
  255.             item: ControlHandle;
  256.             box: Rect;
  257.     begin
  258.         GetDialogItem(theDialog, itemNo, kind, Handle(item), box);
  259. {Check kind}
  260.         kind := BitAnd(kind, 127);
  261.         case kind of
  262.             8, 16: {statText, editText}
  263.                 SetDialogItemText(handle(item), theString);
  264.             0, 1, 2, 4, 5, 6: {button, checkbox, radio - men vad är 4?}
  265.                 SetControlTitle(item, theString);
  266.             otherwise {Övriga har ingen text man kan sätta}
  267.                 SysBeep(1);
  268.         end;{case}
  269.     end;
  270.     function GetTextDItem (theDialog: DialogPtr; itemNo: integer): Str255;
  271.         var
  272.             kind: integer;
  273.             item: ControlHandle;
  274.             box: Rect;
  275.             tmpStr: Str255;
  276.     begin
  277.         GetDialogItem(theDialog, itemNo, kind, Handle(item), box);
  278. {Check kind}
  279.         kind := BitAnd(kind, 127);
  280.         tmpStr := '';
  281.         case kind of
  282.             8, 16: {statText, editText}
  283.                 GetDialogItemText(handle(item), tmpStr);
  284.             0, 1, 2, 4, 5, 6: {button, checkbox, radio…?}
  285.                 GetControlTitle(item, tmpStr);
  286.             otherwise {Övriga har ingen text man kan sätta}
  287.                 SysBeep(1);
  288.         end;{case}
  289.         GetTextDItem := tmpStr;
  290.     end;
  291.     function MyNumToString (l: longint): Str255;
  292.         var
  293.             tmpStr: Str255;
  294.     begin
  295.         NumToString(l, tmpStr);
  296.         MyNumToString := tmpStr;
  297.     end;
  298.  
  299. {Make the new high score dialog}
  300.     procedure AskHigh;
  301.         const
  302.             highDlogID = 129;
  303.         var
  304.             dialog: DialogPtr;
  305.             oldPort: GrafPtr;
  306.             itemHit: integer;
  307.             str: str255;
  308.     begin
  309.         GetPort(oldPort);
  310.         dialog := GetNewDialog(highDlogID, nil, WindowPtr(-1));
  311.         ShowWindow(dialog);
  312.         SelectWindow(dialog);
  313.         SetPort(dialog);
  314.  
  315.         SetTextDItem(dialog, 3, settings^^.player);
  316.         SelectDialogItemText(dialog, 3, 0, 32767);
  317.         itemHit := -1;
  318.         while (itemHit <> 1) and (itemHit <> 2) do { 1=ok, 2=cancel }
  319.             ModalDialog(nil, itemHit);
  320.         if itemHit = 1 then
  321.             begin
  322.                 str := GetTextDItem(dialog, 3);
  323.                 if length(str) > 5 then
  324.                     str[0] := char(5); {snabbaste sättet att korta den!}
  325.                 settings^^.player := str;
  326.                 settings^^.high := score;
  327.             end;
  328.         DisposeDialog(dialog);
  329.         SetPort(oldPort);
  330.     end;
  331.  
  332. {Reuseable sprite movement routine, called from all sprite handling routines. Some sprites use this}
  333. {as handling routine.}
  334.     procedure SATBounce (me: SpritePtr);
  335.     begin
  336.         me^.position.h := me^.position.h + me^.speed.h;
  337.         me^.position.v := me^.position.v + me^.speed.v;
  338.         if me^.position.h < 0 then
  339.             me^.speed.h := abs(me^.speed.h);
  340.         if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  341.             me^.speed.h := -abs(me^.speed.h);
  342.         if me^.position.v < 0 then
  343.             me^.speed.v := abs(me^.speed.v);
  344.         if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  345.             me^.speed.v := -abs(me^.speed.v);
  346.     end;
  347.  
  348. {The same but using fixed-point position, as in HandlePlayer}
  349.     procedure SATFixedBounce (me: C3SpritePtr);
  350.     begin
  351.         me^.fixedPos.h := me^.fixedPos.h + me^.speed.h;
  352.         me^.fixedPos.v := me^.fixedPos.v + me^.speed.v;
  353.  
  354.         me^.position.h := BSR(me^.fixedPos.h, 4); {Shift left 4 steps, i.e. div 16}
  355.         me^.position.v := BSR(me^.fixedPos.v, 4);
  356.  
  357. {Since BSR isn't aritmetic shift, a negative fixedPos will unfortunately result in}
  358. {a very large positive position. This must be accounted for when checking borders}
  359. {- or we could use div, but that is slower.}
  360.  
  361.         if me^.fixedPos.h < 0 then
  362.             begin
  363.                 me^.speed.h := abs(me^.speed.h);
  364.                 me^.position.h := 0;
  365.             end
  366.         else if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  367.             me^.speed.h := -abs(me^.speed.h);
  368.         if me^.fixedPos.v < 0 then
  369.             begin
  370.                 me^.speed.v := abs(me^.speed.v);
  371.                 me^.position.v := 0;
  372.             end
  373.         else if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  374.             me^.speed.v := -abs(me^.speed.v);
  375.     end;
  376.  
  377.  
  378.     procedure HandleTheSprite (me: C3SpritePtr);
  379.     begin
  380.         if me^.speed.h = 0 then
  381.             me^.speed.h := SATRand(32) - SATRand(32);
  382.         if me^.speed.v = 0 then
  383.             me^.speed.v := SATRand(32) - SATRand(32);
  384.         if me^.face = nil then
  385.             begin
  386.                 me^.face := myFace;
  387.                 if me^.face <> nil then
  388.                     me^.hotRect := me^.face^.iconMask.bounds;
  389.             end;
  390.         SATFixedBounce(me);
  391.     end;
  392.  
  393.     procedure RedrawScoreFace;
  394.     begin
  395.         SATSetPortFace(scoreFace);
  396.         EraseRect(scoreFace^.iconMask.bounds);
  397.         MoveTo(2, 14);
  398.         ForeColor(blackColor);
  399.         DrawString('Score:');
  400.         SATDrawLong(score);
  401.         ForeColor(whiteColor);
  402.         MoveTo(0, 12);
  403.         DrawString('Score:');
  404.         SATDrawLong(score);
  405.         ForeColor(blackColor);
  406.         SATSetPortScreen;
  407.         SATSetPortMask(scoreFace);
  408.         EraseRect(scoreFace^.iconMask.bounds);
  409.         MoveTo(0, 12);
  410.         DrawString('Score:');
  411.         SATDrawLong(score);
  412.         MoveTo(2, 14);
  413.         DrawString('Score:');
  414.         SATDrawLong(score);
  415.         SATSetPortScreen;
  416.         SATChangedFace(scoreFace);
  417.     end;
  418.  
  419.     procedure RedrawHighFace;
  420.         var
  421.             str: Str255;
  422.     begin
  423.         str := stringof('High score:', MyNumToString(settings^^.high), ' by ', settings^^.player);
  424.  
  425.         SATSetPortFace(highFace);
  426.         EraseRect(highFace^.iconMask.bounds);
  427.         MoveTo(2, 14);
  428.         ForeColor(blackColor);
  429.         DrawString(str);
  430.         ForeColor(whiteColor);
  431.         MoveTo(0, 12);
  432.         DrawString(str);
  433.         ForeColor(blackColor);
  434.         SATSetPortScreen;
  435.         SATSetPortMask(highFace);
  436.         EraseRect(highFace^.iconMask.bounds);
  437.         MoveTo(0, 12);
  438.         DrawString(str);
  439.         MoveTo(2, 14);
  440.         DrawString(str);
  441.         SATSetPortScreen;
  442.         SATChangedFace(highFace);
  443.     end;
  444.  
  445.     procedure RedrawLastFace;
  446.     begin
  447.         SATSetPortFace(lastface);
  448.         EraseRect(lastface^.iconMask.bounds);
  449.         MoveTo(2, 14);
  450.         DrawString('Last score:');
  451.         SATDrawLong(score);
  452.         ForeColor(whiteColor);
  453.         MoveTo(0, 12);
  454.         DrawString('Last score:');
  455.         SATDrawLong(score);
  456.         ForeColor(blackColor);
  457.         SATSetPortScreen;
  458.         SATSetPortMask(lastface);
  459.         EraseRect(lastface^.iconMask.bounds);
  460.         MoveTo(0, 12);
  461.         DrawString('Last score:');
  462.         SATDrawLong(score);
  463.         MoveTo(2, 14);
  464.         DrawString('Last score:');
  465.         SATDrawLong(score);
  466.         SATSetPortScreen;
  467.         SATChangedFace(lastface);
  468.     end;
  469.  
  470.     var
  471.         playerFace: array[0..15] of FacePtr;
  472.         playerSpeed: array[0..15] of Point;
  473.  
  474.  
  475.  
  476.  
  477.  
  478. {Redraw all player faces. This is separated from InitPlayerFaces since it must be called on}
  479. {depth changes.}
  480.     procedure ReDrawPlayerFaces;
  481.         const
  482.             totalAngle = 240;
  483.         var
  484.             i: integer;
  485.             r, r1, r2, ri: Rect;
  486.             reg1, reg2: RgnHandle;
  487.             pol: PolyHandle;
  488.     begin
  489.         SetRect(r, 0, 0, 40, 40); {Total face size}
  490.         SetRect(r1, 0, 0, 38, 38); {Colored part}
  491.         SetRect(ri, 9, 9, 29, 29); {Colored part, inner circle}
  492.         SetRect(r2, 2, 2, 40, 40); {Shadow}
  493.         for i := 0 to 15 do
  494.             begin
  495.                 reg1 := NewRgn;
  496.                 reg2 := NewRgn;
  497.  
  498. {Generate shape}
  499.                 SATSetPortMask(playerFace[i]);
  500.                 PaintArc(r1, i * 360 div 16 - (360 - totalAngle) div 2, totalAngle);
  501.                 EraseArc(ri, i * 360 div 16 - (360 - totalAngle) div 2, totalAngle); {360-graders-skala}
  502. {$IFC GENERATINGPOWERPC }
  503.                 if noErr <> BitMapToRegion(reg1, playerFace[i]^.iconMask) then{}
  504.                     ;
  505. {$ELSEC}
  506.  
  507. {NOTE: For this call, CW requires that MacIntf is compiled with the SYSTEMSEVENORLATER flag = FALSE!}
  508.                 if noErr <> BitMapToRegionGlue(reg1, playerFace[i]^.iconMask) then{}
  509.                     ;
  510.  
  511. {$ENDC}
  512.                 CopyRgn(reg1, reg2);
  513.                 OffsetRgn(reg2, 2, 2);
  514.  
  515. {Draw face}
  516.                 SATSetPortFace(playerFace[i]);
  517.                 EraseRect(playerFace[i]^.iconMask.bounds);
  518.                 ForeColor(blackColor);
  519.                 PaintRgn(reg2); {black "Shadow"}
  520.                 ForeColor(cyanColor);
  521.                 if gSAT.initDepth > 1 then
  522.                     PaintRgn(reg1) {If we run in color, fill it completely with cyan}
  523.                 else
  524. {$IFC UNDEFINED THINK_PASCAL}
  525.                     FillRgn(reg1, qd.ltGray); {If we run in b/w, a gray pattern looks nicer}
  526. {$ELSEC}
  527.                 FillRgn(reg1, ltGray); {If we run in b/w, a gray pattern looks nicer}
  528. {$ENDC}
  529.                 ForeColor(blueColor);
  530.                 FrameRgn(reg1);
  531.                 ForeColor(blackColor);
  532. {Draw mask}
  533.                 SATSetPortMask(playerFace[i]);
  534.                 EraseRect(playerFace[i]^.iconMask.bounds);
  535.                 PaintRgn(reg1);
  536.                 PaintRgn(reg2);
  537.                 SATSetPortScreen;
  538.                 SATChangedFace(playerFace[i]);
  539.  
  540.                 DisposeRgn(reg1);
  541.                 DisposeRgn(reg2);
  542.             end;
  543.     end;
  544.  
  545. {Create all player faces.}
  546.     procedure InitPlayerFaces;
  547.         var
  548.             i: integer;
  549.             r: Rect;
  550.     begin
  551. {We use crude approximations to the sine/cosine functions we really want.}
  552. {A real game might init the table by using sine and cosine for real, but I don't}
  553. {want to make this harder to read than it already is. A real game would also}
  554. {use more than 16 directions, say 32 or even 64.}
  555.  
  556.         SetPt(playerSpeed[6], 0, -6);
  557.         SetPt(playerSpeed[7], 2, -5);
  558.         SetPt(playerSpeed[8], 4, -4);
  559.         SetPt(playerSpeed[9], 5, -2);
  560.         SetPt(playerSpeed[10], 6, 0);
  561.         SetPt(playerSpeed[11], 5, 2);
  562.         SetPt(playerSpeed[12], 4, 4);
  563.         SetPt(playerSpeed[13], 2, 5);
  564.         SetPt(playerSpeed[14], 0, 6);
  565.         SetPt(playerSpeed[15], -2, 5);
  566.         SetPt(playerSpeed[0], -4, 4);
  567.         SetPt(playerSpeed[1], -5, 2);
  568.         SetPt(playerSpeed[2], -6, 0);
  569.         SetPt(playerSpeed[3], -5, -2);
  570.         SetPt(playerSpeed[4], -4, -4);
  571.         SetPt(playerSpeed[5], -2, -5);
  572.  
  573.         SetPt(playerSpeed[6], 0, -32);
  574.         SetPt(playerSpeed[7], 14, -28);
  575.         SetPt(playerSpeed[8], 22, -22);
  576.         SetPt(playerSpeed[9], 28, -14);
  577.         SetPt(playerSpeed[10], 32, 0);
  578.         SetPt(playerSpeed[11], 28, 14);
  579.         SetPt(playerSpeed[12], 22, 22);
  580.         SetPt(playerSpeed[13], 14, 28);
  581.         SetPt(playerSpeed[14], 0, 32);
  582.         SetPt(playerSpeed[15], -14, 28);
  583.         SetPt(playerSpeed[0], -22, 22);
  584.         SetPt(playerSpeed[1], -28, 14);
  585.         SetPt(playerSpeed[2], -32, 0);
  586.         SetPt(playerSpeed[3], -28, -14);
  587.         SetPt(playerSpeed[4], -22, -22);
  588.         SetPt(playerSpeed[5], -14, -28);
  589.  
  590.         SetRect(r, 0, 0, 40, 40); {Total face size}
  591.         for i := 0 to 15 do
  592.             begin
  593.                 playerFace[i] := SATNewFace(r);
  594.                 SATChangedFace(playerFace[i]);
  595.             end;
  596.         RedrawPlayerFaces;
  597.     end;
  598.  
  599.     procedure HandlePlayer (me: C3SpritePtr);
  600.     begin
  601.         me^.mode := gMode;
  602.         me^.face := playerFace[me^.mode];
  603.  
  604.         me^.fixedPos.h := me^.fixedPos.h + playerSpeed[me^.mode].h;
  605.         me^.fixedPos.v := me^.fixedPos.v + playerSpeed[me^.mode].v;
  606.  
  607.         me^.position.h := BSR(me^.fixedPos.h, 4); {Shift left 4 steps, i.e. div 16}
  608.         me^.position.v := BSR(me^.fixedPos.v, 4);
  609.  
  610.         if me^.fixedPos.h < 0 then
  611.             begin
  612.                 me^.position.h := 0;
  613.                 me^.fixedPos.h := 0;
  614. {gMode := BitAnd(BitAnd(4 - gMode, 15) + 4, 15);}
  615.             end;
  616.         if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  617.             begin
  618.                 me^.position.h := gSAT.offSizeH - me^.hotRect.right;
  619.                 me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  620. {gMode := BitAnd(BitAnd(4 - gMode, 15) + 4, 15);}
  621.             end;
  622.         if me^.fixedPos.v < 0 then
  623.             begin
  624.                 me^.position.v := 0;
  625.                 me^.fixedPos.v := 0;
  626. {gMode := BitAnd(-gMode, 15);}
  627.             end;
  628.         if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  629.             begin
  630.                 me^.position.v := gSAT.offSizeV - me^.hotRect.bottom;
  631.                 me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  632. {gMode := BitAnd(-gMode, 15);}
  633.             end;
  634.     end;
  635.  
  636. {Get a vector from center to center of two sprites}
  637.     function Vector (s1, s2: SpritePtr): Point;
  638.     begin
  639.         Vector.h := s1^.position.h + s1^.face^.iconMask.bounds.right div 2 - s2^.position.h - s2^.face^.iconMask.bounds.right div 2;
  640.         Vector.v := s1^.position.v + s1^.face^.iconMask.bounds.right div 2 - s2^.position.v - s2^.face^.iconMask.bounds.right div 2;
  641.     end;
  642.  
  643. {Squared distance between centers of two sprites}
  644.     function Dist2 (s1, s2: SpritePtr): Longint;
  645.         var
  646.             v: Point;
  647.     begin
  648.         v := Vector(s1, s2);
  649.         Dist2 := v.h * v.h + v.v * v.v;
  650.     end;
  651.  
  652.     procedure CreatePill;
  653.     forward;
  654.  
  655.  
  656. {***Check for hits based on regions - reuseable procedure!***}
  657.     function RegionHitTest (s1, s2: SpritePtr): Boolean;
  658.         var
  659.             r1, r2: RgnHandle;
  660.     begin
  661. {We know that out hotRects coincide. However, that doesn't mean that we must take it as a}
  662. {collision! Rather, we can do more processing here to decide whether or not it was a collision.}
  663. {In this case, we copy the mask regions of each sprite, offset them to the proper positions,}
  664. {and check if they, too, overlap!}
  665. {}
  666. {Do you think we are doing double work, both dealing with hotRects and the regions? If you do,}
  667. {let me explain some more. The idea is that SAT checks the hotRects for you, which takes away}
  668. {next to all false hits. Checking hotRects is *fast*, so that's what we can afford to do all-to-all}
  669. {(or all-to-near, depending on the chosen search mode). Once a *possible* collision is detected,}
  670. {we can spend some time analyzing it further!}
  671.  
  672. {First of all, let's do some error checking. We could also have done this when loading the faces.}
  673. {Most programs won't have to bother whether or not the regions have been generated}
  674. {successfully, but when using them this way, they must exist or we may get a crash.}
  675.         if (s1^.face^.maskRgn = nil) or (s2^.face^.maskRgn = nil) then
  676.             begin
  677.                 SATReportStr('Error: No mask region!');
  678.                 exit(RegionHitTest);
  679.             end;
  680.  
  681. {Make copies of the mask regions and offset them to the proper places.}
  682.         r1 := NewRgn;
  683.         r2 := NewRgn;
  684.         CopyRgn(s1^.face^.maskRgn, r1);
  685.         CopyRgn(s2^.face^.maskRgn, r2);
  686.         OffsetRgn(r1, s1^.position.h, s1^.position.v);
  687.         OffsetRgn(r2, s2^.position.h, s2^.position.v);
  688.  
  689.         SectRgn(r1, r2, r1);                    {Is there any overlap?}
  690.  
  691. {If empty, no collision, otherwise, handle the collision!}
  692.         RegionHitTest := not EmptyRgn(r1);
  693.  
  694.         DisposeRgn(r1);
  695.         DisposeRgn(r2);
  696.     end;
  697.  
  698. {Collision handling for the player sprite}
  699.     procedure HitPlayer (me, him: SpritePtr);
  700.         var
  701.             v: Point;
  702.     begin
  703.         if RegionHitTest(me, him) then {Do the sprites *really* overlap?}
  704.             begin
  705.  
  706.                 if Dist2(me, him) > 60 then
  707.                     begin
  708. {Hit too far out, so let's call it the outside. Bounce away him.}
  709. {We could make more efforts here for a good bounce.}
  710.                         him^.position.h := him^.position.h + me^.speed.h;
  711.                         him^.speed.h := -him^.speed.h + me^.speed.h;
  712.                         him^.position.v := him^.position.v + me^.speed.v;
  713.                         him^.speed.v := -him^.speed.v + me^.speed.v;
  714. {Finally, make sure the other is moving *away* from us!}
  715. {And when we're at it, why not move it just a little, too?}
  716. {Yuck, this is ugly! Yup, careless programming. Hack, hack!}
  717.                         v := Vector(me, him);
  718.                         if v.h > 0 then
  719.                             begin
  720.                                 if him^.speed.h > 0 then
  721.                                     him^.speed.h := -him^.speed.h;
  722.                                 him^.position.h := him^.position.h - 1;
  723.                             end
  724.                         else
  725.                             begin
  726.                                 if v.h < 0 then
  727.                                     if him^.speed.h < 0 then
  728.                                         him^.speed.h := -him^.speed.h;
  729.                                 him^.position.h := him^.position.h + 1;
  730.                             end;
  731.                         if v.v > 0 then
  732.                             begin
  733.                                 if him^.speed.v > 0 then
  734.                                     him^.speed.v := -him^.speed.v;
  735.                                 him^.position.v := him^.position.v - 1;
  736.                             end
  737.                         else
  738.                             begin
  739.                                 if v.v < 0 then
  740.                                     if him^.speed.v < 0 then
  741.                                         him^.speed.v := -him^.speed.v;
  742.                                 him^.position.v := him^.position.v + 1;
  743.                             end;
  744.  
  745.                     end
  746.                 else
  747.                     begin
  748. {This looks like inside! Let's eat him.}
  749.                         score := score + 1;
  750.                         RedrawScoreFace;
  751.                         him^.task := nil;
  752.                         setCount := setCount - 1;
  753.                         if setCount < 2 then
  754.                             CreatePill; {There should always be pills left!}
  755.                         SATSoundPlay(bippH, 1, true);
  756.                     end; {Dist2}
  757.             end; {RegionHitTest}
  758.  
  759.     end;
  760.  
  761. {Create the score face}
  762.     procedure InitScoreFace;
  763.         var
  764.             r: Rect;
  765.     begin
  766.         SetRect(r, 0, 0, 80, 14);{}
  767.         scoreFace := SATNewFace(r);
  768.         SATChangedFace(scoreFace);
  769.         SetRect(r, 0, 0, 200, 16);{}
  770.         highFace := SATNewFace(r);
  771.         SATChangedFace(highFace);
  772.         SetRect(r, 0, 0, 120, 16);{}
  773.         lastFace := SATNewFace(r);
  774.         SATChangedFace(lastFace);
  775.     end;
  776.  
  777.     procedure SetupDummy (me: SpritePtr);
  778.     begin
  779.         me^.task := @SATBounce;
  780.     end;
  781.  
  782.     procedure SetupSmall (me: C3SpritePtr);
  783.     begin
  784.         me^.face := myFace;
  785.         me^.hotRect := me^.face^.iconMask.bounds;
  786.         me^.task := @HandleTheSprite;
  787.  
  788.         me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  789.         me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  790.     end;
  791.  
  792.     procedure SetupPlayer (me: C3SpritePtr);
  793.     begin
  794.         me^.face := playerFace[0];
  795.         me^.hotRect := me^.face^.iconMask.bounds;
  796.         me^.task := @HandlePlayer;
  797.         me^.hitTask := @HitPlayer;
  798.  
  799.         me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  800.         me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  801.     end;
  802.  
  803.  
  804.     procedure CreatePill;
  805.         var
  806.             sp: SpritePtr;
  807.     begin
  808.         sp := SATNewSprite(-1, SATRand(gSAT.offSizeH - 32), SATRand(gSAT.offSizeV - 32), @SetupSmall);
  809.         setCount := setCount + 1; {Number of active pills}
  810.     end;
  811.  
  812.  
  813.     procedure NewSet;
  814.         var
  815.             sp: SpritePtr;
  816.             i: integer;
  817.     begin
  818. {Kill all sprites}
  819.         while gSAT.sRoot <> nil do
  820.             SATKillSprite(gSAT.sRoot);
  821.  
  822. {Create the pills}
  823.         for i := 1 to 10 do
  824.             CreatePill;
  825.         if settings^^.high > 7 then
  826.             for i := 8 to settings^^.high do
  827.                 CreatePill;
  828.  
  829.         sp := SATNewSprite(0, SATRand(gSAT.offSizeH - 32), SATRand(gSAT.offSizeV - 32), @SetupDummy);
  830.         RedrawScoreFace;
  831.         sp^.face := scoreFace;
  832.         repeat
  833.             sp^.speed.h := SATRand(5) - 2
  834.         until sp^.speed.h <> 0;
  835.         repeat
  836.             sp^.speed.v := SATRand(3) - 1
  837.         until sp^.speed.v <> 0;
  838.         sp^.hotRect := sp^.face^.iconMask.bounds;
  839. {Hoppsan- fattas nåt!}
  840.  
  841.         sp := SATNewSprite(1, (gSAT.offSizeH - 32) div 2, (gSAT.offSizeV - 32) div 2, @SetupPlayer);
  842.         gMode := 0;
  843.  
  844.         SATBackChanged(gSAT.bounds);
  845.         FlushEvents(6, 0); {Glöm klick från förra uppsättningen!}
  846.         if not (TickCount > gameStartTime + kGameTime) then {Om tiden INTE är ute så skall vi ändra!}
  847.             lastSetStartTime := TickCount;
  848.     end;
  849.  
  850. {An example of how you can (with some effort) scale a sprite.}
  851.     procedure ScaleWelcomeFace;
  852.         var
  853.             srcFacePort, destFacePort: GrafPtr;
  854.             i: integer;
  855.             scaleRect: Rect;
  856.     begin
  857. {Get the rectangle of the original}
  858.         scaleRect := welcomeFace^.iconMask.bounds;
  859.  
  860.         for i := 0 to 31 do
  861.             begin
  862. {SetPortFace to the source. This must be done each turn since ChangedFace changes it.}
  863.                 SATSetPortFace(welcomeFace); {Set the FIRST of SAT's two internal face-ports to the original face.}
  864.                 GetPort(srcFacePort); {Get the port.}
  865. {Modify the size}
  866.                 scaleRect.bottom := scaleRect.bottom - 2;
  867.                 scaleRect.right := scaleRect.right - 2;
  868. {Create the new face}
  869.                 if scaledFace[i] = nil then
  870.                     scaledFace[i] := SATNewFace(scaleRect);
  871. {Get a port to it}
  872.                 SATSetPortFace2(scaledFace[i]); {Set the SECOND of SAT's two internal face ports to the new face.}
  873.                 GetPort(destFacePort); {Get the port.}
  874. {Copy the image}
  875.                 CopyBits(srcFacePort^.portBits, destFacePort^.portBits, welcomeFace^.iconMask.bounds, scaleRect, srcCopy + ditherCopy, nil);
  876.                 CopyBits(welcomeFace^.iconMask, scaledFace[i]^.iconMask, welcomeFace^.iconMask.bounds, scaleRect, srcCopy, nil);
  877.                 SATChangedFace(scaledFace[i]); {Done changing it. Tell SAT that it may do whatever it needs.}
  878.             end; {for}
  879.     end; {ScaleWelcomeFace}
  880.  
  881.     procedure WindUpdate (whatever: Boolean);
  882.         var
  883.             savePort: GrafPtr;
  884.             saveDev: GDHandle;
  885.     begin
  886.         if SATDepthChangeTest then
  887. {IMPORTANT! We must redraw all internally generated faces on depth changes!}
  888.             begin
  889.                 ReDrawPlayerFaces;
  890.                 RedrawScoreFace;
  891.                 RedrawHighFace;
  892.                 RedrawLastFace;
  893.                 ScaleWelcomeFace;
  894.  
  895. {We also have to redraw the background, since it's not a PICT (in which case that is automatic)}
  896.                 GetPort(savePort);
  897.                 if gSAT.colorFlag then
  898.                     saveDev := GetGDevice;
  899.                 SATSetPortBackScreen;
  900.                 SATPenPat(bgPat);
  901.                 PaintRect(gSAT.backScreen.port^.portRect);
  902.                 PenNormal;
  903.                 CopyBits(gSAT.backScreen.port^.portBits, gSAT.offScreen.port^.portBits, gSAT.offScreen.port^.portRect, gSAT.offScreen.port^.portRect, srcCopy, nil);
  904.                 SetPort(savePort);
  905.                 if gSAT.colorFlag then
  906.                     SetGDevice(saveDev);
  907.  
  908.             end;
  909.         SATRedraw;
  910.     end;
  911.  
  912.     procedure WindClose;
  913.     begin
  914.         SkelWhoa;
  915.     end;
  916.  
  917.     procedure WindMouse (where: Point; when: Longint; modifiers: integer);
  918.         var
  919.             found, sp: SpritePtr;
  920.             anyLeft: Boolean;
  921.             myRegion: RgnHandle;
  922.     begin
  923. {Not needed for the game, but note that we can check the mask region of a sprite}
  924. {towards a mouse click as well as a colliding sprite! For demonstrating this, mouse}
  925. {clicks are processed, and if a sprite is hit, a SysBeep is made. Try this by clicking}
  926. {in and around the "Hello" sprite!}
  927.  
  928.         myRegion := NewRgn;
  929.         sp := gSAT.sRoot;
  930.         found := nil;
  931.         while sp <> nil do                                        {Search through the sprite list}
  932.             begin
  933.                 if PtInRect(where, sp^.r) then                        {We are in the rect!}
  934.                     if sp^.face <> nil then                            {Does it have a face at all? Remember it's legal not to have one!}
  935.                         if sp^.face^.maskRgn <> nil then                {Does that face have a mask region? It should, but…}
  936.                             begin
  937.                                 CopyRgn(sp^.face^.maskRgn, myRegion);    {Copy the mask region}
  938.                                 OffsetRgn(myRegion, sp^.position.h, sp^.position.v);        {Offset it to the position of the sprite}
  939.                                 if PtInRgn(where, myRegion) then            {Are we in the region?}
  940.                                     found := sp;                                    {Yes!}
  941.                             end;
  942.                 sp := sp^.next;                                        {Next sprite…}
  943.             end;
  944.         if found <> nil then
  945.             SysBeep(1);                                            {We hit something. Tell us so.}
  946.         DisposeRgn(myRegion);
  947.     end;
  948.  
  949.  
  950.     procedure WindKey (theKey: char; theMods: integer);
  951.     begin
  952. {Hard-coded keys; real games have customizable keys.}
  953.         case theKey of
  954.             ',', 'z', '1': 
  955.                 gMode := BitAnd(gMode - 1, 15);
  956.             '.', 'x', '2': 
  957.                 gMode := BitAnd(gMode + 1, 15);
  958.             otherwise
  959.         end; {case}
  960.         ObscureCursor; {Hide the cursor until the mouse is moved.}
  961.     end;
  962.  
  963.     procedure SetupSAT (theWind: WindowPtr);{Calls SATCustomInit and paints the background with a pattern}
  964.         var
  965.             savePort: SATPort;
  966.             r: Rect;
  967.     begin
  968.         SATGetPort(savePort);
  969.  
  970.         SetPort(theWind);
  971.         r := theWind^.portRect;
  972.         OffsetRect(r, -r.left, -r.top);
  973.         SATCustomInit(0, 0, r, theWind, nil, false, false, false, true, false); {Nytt försök!}
  974. {SATCustomInit(0, 0, theWind^.portRect, theWind, nil, false, false, false, true, false); {Nytt försök!}
  975.  
  976. {We use a customized sprite record! Thus, we must SetSpriteSize before creating sprites!}
  977.         SATSetSpriteRecSize(sizeof(C3Sprite));
  978.  
  979.         if bgPat = nil then
  980.             bgPat := SATGetPat(128);
  981.         if bgPat = nil then
  982.             Barf;
  983.  
  984.         SATSetPortBackScreen;
  985.         SATPenPat(bgPat);
  986.         PaintRect(gSAT.backScreen.port^.portRect);
  987.         PenNormal;
  988.         CopyBits(gSAT.backScreen.port^.portBits, gSAT.offScreen.port^.portBits, gSAT.offScreen.port^.portRect, gSAT.offScreen.port^.portRect, srcCopy, nil);
  989.  
  990.         SATSetPort(savePort);
  991.         CopyBits(gSAT.backScreen.port^.portBits, gSAT.wind.port^.portBits, gSAT.wind.port^.portRect, gSAT.wind.port^.portRect, srcCopy, nil);
  992.  
  993.         if SkelWindow(theWind, @WindMouse, @WindKey, @WindUpdate, nil, @WindClose, nil, nil, false) then
  994.             ;
  995.  
  996.     end;
  997.  
  998.     procedure SetupWindow;
  999.         var
  1000.             slaskWind, theWind: WindowPtr;
  1001.             tmpWorld: SysEnvRec;
  1002.             tmpCol: Boolean;
  1003. {r: Rect;}
  1004. {peek: WindowPeek;}
  1005.     begin
  1006.         tmpCol := false;
  1007.         if noErr = SysEnvirons(1, tmpWorld) then
  1008.             tmpCol := tmpWorld.hasColorQD;
  1009.  
  1010.         if tmpCol then
  1011.             theWind := GetNewCWindow(theWindRes, nil, WindowPtr(-1))
  1012.         else
  1013.             theWind := GetNewWindow(theWindRes, nil, WindowPtr(-1));
  1014.  
  1015. {peek := WindowPeek(theWind);}
  1016.  
  1017.         if theWind = nil then
  1018.             Barf;
  1019.  
  1020. {MoveWindow(theWind, 50, 50, false);}
  1021.  
  1022. {r := WindowPeek(theWind)^.contRgn^^.rgnBBox;}
  1023.  
  1024. {slaskWind := theWind;}
  1025.  
  1026.         SetupSAT(theWind); {Calls SATCustomInit and paints the background with a pattern}
  1027.  
  1028.         SATSetPortScreen;
  1029.         ShowWindow(gSAT.wind.port);
  1030.         SelectWindow(gSAT.wind.port);
  1031.         SATRedraw;
  1032.     end;
  1033.  
  1034. {The task the welcome sprite has while zooming.}
  1035.     procedure ZoomWelcome (me: SpritePtr);
  1036.     begin
  1037.         me^.mode := me^.mode + 1;
  1038. {Compensate for the size change to make it centered in one place.}
  1039.         me^.position.h := me^.position.h - 1;
  1040.         me^.position.v := me^.position.v - 1;
  1041.         if me^.mode >= 32 then
  1042.             begin
  1043.                 me^.face := welcomeFace;
  1044.                 me^.task := @SATBounce;
  1045.             end
  1046.         else
  1047.             me^.face := scaledFace[32 - me^.mode];
  1048.     end;
  1049.  
  1050. {Initialize faces.}
  1051.     procedure InitSpriteFaces;
  1052.         var
  1053.             i: integer;
  1054.     begin
  1055.         myFace := SATGetFace(128);
  1056.         if myFace = nil then
  1057.             Barf;
  1058.         welcomeFace := SATGetFace(138);
  1059.         if welcomeFace = nil then
  1060.             Barf;
  1061. {We don't HAVE to bail out when a face fails to load - the program will stll wor, but that face will}
  1062. {not be visible.}
  1063.         ScaleWelcomeFace;
  1064.     end;
  1065.  
  1066.     var
  1067.         lastTime: Longint;
  1068.  
  1069. {DirtyWork is called from TransSkel}
  1070.     procedure DirtyWork;
  1071.         var
  1072.             sp: SpritePtr;
  1073.             ph: PicHandle;
  1074.             r: Rect;
  1075.             savePort: GrafPtr;
  1076.             saveDev: GDHandle;
  1077.     begin
  1078. {We can check TickCount as usual, since we never know how often we get null events.}
  1079.         if lastTime + 1 < TickCount then
  1080.             begin
  1081.                 SATRun(false);
  1082.                 lastTime := tickCount;
  1083.             end;
  1084.  
  1085.         if gameRunning then
  1086.             begin
  1087. {Timebar}
  1088.                 GetPort(savePort);
  1089.                 if gSAT.colorFlag then
  1090.                     saveDev := GetGDevice;
  1091.                 SATSetPortBackScreen;
  1092. {I *should* change only the part that actually changes!}
  1093.                 r := gSAT.wind.port^.portRect;
  1094.                 SATBackChanged(r);
  1095.                 r.right := 5;
  1096.                 r.top := r.bottom * (lastSetStartTime + kGameTime - TickCount) div kGameTime;
  1097.                 ForeColor(redColor); {Quickest way to get a color.}
  1098.                 PaintRect(r);
  1099.                 r.bottom := r.top;
  1100.                 r.top := 0;
  1101.                 SATPenPat(bgPat);
  1102.                 PaintRect(r);
  1103.                 PenNormal;
  1104.  
  1105.                 SetPort(savePort);
  1106.                 if gSAT.colorFlag then
  1107.                     SetGDevice(saveDev);
  1108. {end of Timebar}
  1109.  
  1110.                 if TickCount > lastSetStartTime + kGameTime then
  1111.                     begin
  1112.                         SATSoundPlay(baeH, 5, true);
  1113. {NewSet;}
  1114.  
  1115.                         if TickCount > gameStartTime + kGameTime then
  1116.                             begin
  1117.                                 if score > settings^^.high then
  1118.                                     begin
  1119. {settings^^.high := score;}
  1120.                                         SATSoundEvents;
  1121.                                         AskHigh;
  1122.                                         ChangedResource(Handle(settings));
  1123.                                     end;
  1124.  
  1125. {Kill all sprites}
  1126.                                 while gSAT.sRoot <> nil do
  1127.                                     SATKillSprite(gSAT.sRoot);
  1128.  
  1129.                                 RedrawHighFace;
  1130.                                 RedrawLastFace;
  1131. {Time for breaking some of my conventions! The stuff below should be done in "setup" and "handle"}
  1132. {routines, as recommened in the manual and done in other demos - but if we want to mess up the code,}
  1133. {we are free to do so! The sprites below set up their faces and speeds right here, and share a common}
  1134. {handling routine (SATBounce).}
  1135.  
  1136. {Make the "hello" sprite}
  1137.                                 sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 2, @SetupDummy);
  1138.                                 sp^.face := welcomeFace;
  1139.                                 repeat
  1140.                                     sp^.speed.h := SATRand(3) - 1
  1141.                                 until sp^.speed.h <> 0;
  1142.                                 repeat
  1143.                                     sp^.speed.v := SATRand(3) - 1
  1144.                                 until sp^.speed.v <> 0;
  1145.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1146.                                 sp^.task := @ZoomWelcome;
  1147. {High score sprite}
  1148.                                 sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 4, @SetupDummy);
  1149.                                 sp^.face := highFace;
  1150.                                 repeat
  1151.                                     sp^.speed.h := SATRand(7) - 3
  1152.                                 until sp^.speed.h <> 0;
  1153.                                 repeat
  1154.                                     sp^.speed.v := SATRand(3) - 1
  1155.                                 until sp^.speed.v <> 0;
  1156.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1157. {Last score sprite}
  1158.                                 sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 3, @SetupDummy);
  1159.                                 sp^.face := lastFace;
  1160.                                 repeat
  1161.                                     sp^.speed.h := SATRand(7) - 3
  1162.                                 until sp^.speed.h <> 0;
  1163.                                 repeat
  1164.                                     sp^.speed.v := SATRand(3) - 1
  1165.                                 until sp^.speed.v <> 0;
  1166.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1167.  
  1168.                                 SATSetPortScreen;
  1169.                                 SATRedraw; {Just to make sure killed sprites are erased}
  1170.                                 gameRunning := false;
  1171.                             end;
  1172.  
  1173.                     end;
  1174.             end;
  1175.  
  1176.         if not gameRunning then
  1177.             if gSAT.sRoot = nil then
  1178.                 begin
  1179. {Messy code for setting up the "hello" sprite - which is why I recommend the use of setup routines.}
  1180.                     sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 2, @SetupDummy);
  1181.                     sp^.face := welcomeFace;
  1182.                     repeat
  1183.                         sp^.speed.h := SATRand(3) - 1
  1184.                     until sp^.speed.h <> 0;
  1185.                     repeat
  1186.                         sp^.speed.v := SATRand(3) - 1
  1187.                     until sp^.speed.v <> 0;
  1188.                     sp^.hotRect := sp^.face^.iconMask.bounds;
  1189.                     sp^.task := @ZoomWelcome;
  1190.                 end;
  1191.     end;
  1192.  
  1193.     procedure InitHigh;
  1194.     begin
  1195.         settings := SettingsHnd(GetResource('Sett', 0));
  1196.         if settings = nil then {Didn't exist - create it!}
  1197.             begin
  1198.                 settings := SettingsHnd(NewHandle(Sizeof(SettingsRec)));
  1199.                 if settings = nil then
  1200.                     begin
  1201.                         SysBeep(1);
  1202.                         halt;
  1203.                     end;
  1204.                 settings^^.high := 0;
  1205.                 AddResource(handle(settings), 'Sett', 0, '');
  1206.             end
  1207.         else {Did exist - check the size!}
  1208.             if GetHandleSize(Handle(settings)) < sizeof(SettingsRec) then
  1209.                 SetHandleSize(Handle(settings), sizeof(SettingsRec));
  1210.     end;
  1211.  
  1212.     procedure DoFileMenu (item: integer);
  1213.     begin
  1214.         case item of
  1215.             newGameItem: 
  1216.                 begin
  1217.                     score := 0;
  1218.                     gameRunning := true;
  1219.                     gameStartTime := TickCount;
  1220.                     lastSetStartTime := TickCount;
  1221.                     setCount := 0;
  1222.                     NewSet;
  1223.  
  1224.                     ObscureCursor; {Hide the cursor until the mouse is moved.}
  1225.                 end;
  1226.             clearHighItem: 
  1227.                 if SATQuestionStr('Set the high score to zero?') then
  1228.                     begin
  1229.                         settings^^.high := 0;
  1230.                         ChangedResource(handle(settings));
  1231.                     end;
  1232.             otherwise
  1233.                 SkelWhoa;
  1234.         end;
  1235.     end;
  1236.  
  1237.     procedure DoShapeMenu (item: integer);
  1238.         const
  1239.             wide = 1;
  1240.             tall = 2;
  1241.         var
  1242.             p: Point;
  1243.     begin
  1244.         p := gSAT.wind.port^.portRect.botRight;
  1245.         case item of
  1246.             wide: 
  1247.                 if gSAT.wind.port^.portRect.bottom > gSAT.wind.port^.portRect.right then
  1248.                     begin
  1249.                         CheckItem(shapeMenu, wide, true);
  1250.                         CheckItem(shapeMenu, tall, false);
  1251.                         SizeWindow(gSAT.wind.port, p.v, p.h, false); {swap size}
  1252.                         SATKill;
  1253.                         SetupSAT(gSAT.wind.port);
  1254.                         gameRunning := false;
  1255.                     end;
  1256.             tall: 
  1257.                 if gSAT.wind.port^.portRect.bottom < gSAT.wind.port^.portRect.right then
  1258.                     begin
  1259.                         CheckItem(shapeMenu, tall, true);
  1260.                         CheckItem(shapeMenu, wide, false);
  1261.                         SizeWindow(gSAT.wind.port, p.v, p.h, false); {swap size}
  1262.                         SATKill;
  1263.                         SetupSAT(gSAT.wind.port);
  1264.                         gameRunning := false;
  1265.                     end;
  1266.             otherwise
  1267.                 SysBeep(1);
  1268.         end;{case}
  1269.     end;
  1270.  
  1271.     procedure SetUpMenus;
  1272.     begin
  1273.         SkelApple('About CollisionIII…', @DoAbout);
  1274.         fileMenu := GetMenu(fileMenuRes);
  1275.         if fileMenu = nil then
  1276.             Barf;
  1277.         if SkelMenu(fileMenu, @DoFileMenu, nil, true) then
  1278.             ;
  1279.         shapeMenu := GetMenu(shapeMenuRes);
  1280.         if shapeMenu = nil then
  1281.             Barf;
  1282.         if SkelHMenu(shapeMenu, @DoShapeMenu, nil) then {Install as hierarcical menu}
  1283.             ;
  1284.         CheckItem(shapeMenu, 1, true); {Check "wide"}
  1285.     end;
  1286.  
  1287. begin
  1288.     SkelInit(6, nil);
  1289.     SkelSetSleep(0); {Tell TransSkel that we want attention as often as possible.}
  1290.     SetupMenus;
  1291.     SetupWindow;
  1292.  
  1293.     InitHigh;
  1294.     InitSpriteFaces;
  1295.     InitScoreFace;
  1296.     InitPlayerFaces;
  1297.  
  1298.     SkelBackground(@DirtyWork);
  1299.     lastTime := TickCount;
  1300. {$IFC UNDEFINED THINK_PASCAL}
  1301.     qd.randSeed := TickCount;
  1302. {$ELSEC}
  1303.     randSeed := TickCount;
  1304. {$ENDC}
  1305.  
  1306.     Synth; {Build sounds!}
  1307.     SATSoundShutup;
  1308.  
  1309.     SkelMain;
  1310.     SkelClobber;
  1311.     SATSoundShutup;
  1312. end.